/*-----------------------------------------------------------------------------------*/
/* Nuvoton Technology Corporation confidential                                       */
/*                                                                                   */
/* Copyright (c) 2008 by Nuvoton Technology Corporation                              */
/* All rights reserved                                                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
/* 
 * Driver for FMI devices
 * SD layer glue code
 *
 */

#ifdef ECOS
#include "stdlib.h"
#include "string.h"
#include "drv_api.h"
#include "diag.h"
#include "wbtypes.h"
#include "wbio.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wblib.h"
#endif

#include "nuc900_reg.h"
#include "fmi.h"
#include "nuc900_sic.h"
#include "nvtfat.h"

DISK_DATA_T SD_DiskInfo;

FMI_SD_INFO_T *pSD0 = NULL;
FMI_SD_INFO_T *pSD1 = NULL;

PDISK_T *pDisk_SD0 = NULL;
PDISK_T *pDisk_SD1 = NULL;
extern INT  fsPhysicalDiskConnected(PDISK_T *pDisk);


static INT  sd_disk_init(PDISK_T  *lDisk)
{
	return 0;
}


static INT  sd_disk_ioctl(PDISK_T *lDisk, INT control, VOID *param)
{
	return 0;
}

static INT  sd_disk_read(PDISK_T *pDisk, UINT32 sector_no, INT number_of_sector, UINT8 *buff)
{
	int status;	

	if (pDisk == pDisk_SD0)
	{
		outpw(REG_SDCSR, inpw(REG_SDCSR) & 0x9fffffff);
		status = fmiSD_Read_in(pSD0, sector_no, number_of_sector, (unsigned int)buff);
	}
	else if (pDisk == pDisk_SD1)
	{
		outpw(REG_SDCSR, (inpw(REG_SDCSR) & 0x9fffffff) | 0x40000000);
		status = fmiSD_Read_in(pSD1, sector_no, number_of_sector, (unsigned int)buff);
	}

	if (status != Successful)
		return status;

	return FS_OK;
}

static INT  sd_disk_write(PDISK_T *pDisk, UINT32 sector_no, INT number_of_sector, UINT8 *buff, BOOL IsWait)
{
	int status;

	if (pDisk == pDisk_SD0)
	{
		outpw(REG_SDCSR, inpw(REG_SDCSR) & 0xbfffffff);
		status = fmiSD_Write_in(pSD0, sector_no, number_of_sector, (unsigned int)buff);
	}
	else if (pDisk == pDisk_SD1)
	{
		outpw(REG_SDCSR, inpw(REG_SDCSR) | 0x40000000);
		status = fmiSD_Write_in(pSD1, sector_no, number_of_sector, (unsigned int)buff);
	}

	if (status != Successful)
		return status;

	return FS_OK;
}



STORAGE_DRIVER_T  _SDDiskDriver = 
{
	sd_disk_init,
	sd_disk_read,
	sd_disk_write,
	sd_disk_ioctl,
};


INT  fmiInitSDDevice(UINT32 uChipSel)   /* int sd_init_onedisk(INT i) */
{
	PDISK_T *pDisk;
	int volatile rate, i;

	// enable SD
	outpw(REG_CLKEN1, inpw(REG_CLKEN1) | 0x02);
	outpw(REG_FMICSR, 0x02);

	outpw(REG_SDCSR, 0x01010000);

	if (_fmi_uFMIReferenceClock >= 133000)
	{
		outpw(REG_CLKSEL, inpw(REG_CLKSEL) | 0x1000);
		_fmi_uFMIReferenceClock = _fmi_uFMIReferenceClock >> 1;
	}
    /******************************/
    /* [2012/11/15] for diag test */
    i = sysGetTicks(TIMER0);
    while(1)
    {
        if ((sysGetTicks(TIMER0) - i) > 15)
            break;
    }
    /******************************/

	if (uChipSel == 0)
	{
		// enable SD0 power
		outpw(0xB000000C, (inpw(0xB000000C) & 0xffffff0f)|0xa0);
		outpw(0xb8003014, 0x100);	// set gpiod-8 to output
		outpw(0xb8003018, inpw(0xb8003018) | 0x100);	// set gpiod-8 output high

		pSD0 = malloc(sizeof(FMI_SD_INFO_T));
		memset((char *)pSD0, 0, sizeof(FMI_SD_INFO_T));

		outpw(REG_SDCSR, inpw(REG_SDCSR) & 0x9fffffff);
#ifdef _USE_DAT3_DETECT_
		outpw(REG_SDIER, inpw(REG_SDIER) & 0xbfffffff); // select DAT3 detect
		if (inpw(REG_SDISR) & 0x10000)
		{
			outpw(0xb8003018, inpw(0xb8003018) & 0xfffffeff);	// set gpiod-8 output low
			pSD0->bIsCardInsert = TRUE;
			outpw(REG_SDIER, inpw(REG_SDIER)&0xfffffeff);
		}
		else
		{
			pSD0->bIsCardInsert = FALSE;
			outpw(REG_SDIER, inpw(REG_SDIER)|0x100);
			return FMI_NO_SD_CARD;
		}
#else
		outpw(REG_SDIER, inpw(REG_SDIER)|0x40000000); // select GPIO detect
		outpw(REG_SDIER, inpw(REG_SDIER)|0x100);
		if (inpw(REG_SDISR) & 0x10000)
		{
			pSD0->bIsCardInsert = FALSE;
			return FMI_NO_SD_CARD;
		}
		else
		{
			outpw(0xb8003018, inpw(0xb8003018) & 0xfffffeff);	// set gpiod-8 output low
			pSD0->bIsCardInsert = TRUE;
		}
#endif
		i = sysGetTicks(TIMER0);
		while(1)
		{
			if ((sysGetTicks(TIMER0) - i) > 1)
				break;
		}

		if (fmiSD_Init(pSD0) < 0)
			return FMI_SD_INIT_ERROR;

		/* divider */
		if (pSD0->uCardType == 3)
			rate = _fmi_uFMIReferenceClock / 20000;
		else
			rate = _fmi_uFMIReferenceClock / 25000;

#ifdef _FPGA_
		if (rate == 0)
			rate = 1;
		else
#endif
		if (pSD0->uCardType == 3)
		{
			if ((_fmi_uFMIReferenceClock % 20000) == 0)
				rate = rate - 1;
		}
		else
		{
			if ((_fmi_uFMIReferenceClock % 25000) == 0)
				rate = rate - 1;
		}
		outpw(REG_CLKDIV1, (inpw(REG_CLKDIV1) & 0x00ff) | (rate << 8));		// bit8 ~ 15

		/* init SD interface */
		fmiGet_SD_info(pSD0, &SD_DiskInfo);

		if (fmiSelectCard(pSD0))
			return FMI_SD_SELECT_ERROR;
	}
	else if (uChipSel == 1)
	{
		// enable SD1 power
		outpw(0xB000000C, (inpw(0xB000000C) & 0xffe3ffff)|0x80001);
		outpw(0xb8003044, 0x20);	// set gpiog-5 to output
		outpw(0xb8003048, inpw(0xb8003048) | 0x20);	// set gpiog-5 output high

		pSD1 = malloc(sizeof(FMI_SD_INFO_T));
		memset((char *)pSD1, 0, sizeof(FMI_SD_INFO_T));

		outpw(REG_SDCSR, (inpw(REG_SDCSR)&0x9fffffff) | 0x40000000);
#ifdef _USE_DAT3_DETECT_
		outpw(REG_SDIER, inpw(REG_SDIER) & 0x7fffffff); // select DAT3 detect
		if (inpw(REG_SDISR) & 0x20000)
		{
			pSD1->bIsCardInsert = TRUE;
			outpw(0xb8003048, inpw(0xb8003048) & 0xffffffdf);	// set gpiog-5 output low
			outpw(REG_SDIER, inpw(REG_SDIER)&0xfffffdff);
		}
		else
		{
			pSD1->bIsCardInsert = FALSE;
			outpw(REG_SDIER, inpw(REG_SDIER)|0x200);
			return FMI_NO_SD_CARD;
		}
#else
		outpw(REG_SDIER, inpw(REG_SDIER)|0x80000000); // select GPIO detect
		outpw(REG_SDIER, inpw(REG_SDIER)|0x200);
		if (inpw(REG_SDISR) & 0x20000)
		{
			pSD1->bIsCardInsert = FALSE;
			return FMI_NO_SD_CARD;
		}
		else
		{
			outpw(0xb8003048, inpw(0xb8003048) & 0xffffffdf);	// set gpiog-5 output low
			pSD1->bIsCardInsert = TRUE;
		}
#endif
		i = sysGetTicks(TIMER0);
		while(1)
		{
			if ((sysGetTicks(TIMER0) - i) > 1)
				break;
		}

		if (fmiSD_Init(pSD1) < 0)
			return FMI_SD_INIT_ERROR;

		/* divider */
		if (pSD0->uCardType == 3)
			rate = _fmi_uFMIReferenceClock / 20000;
		else
			rate = _fmi_uFMIReferenceClock / 25000;
#ifdef _FPGA_
		if (rate == 0)
			rate = 1;
		else
#endif
		if (pSD0->uCardType == 3)
		{
			if ((_fmi_uFMIReferenceClock % 20000) == 0)
				rate = rate - 1;
		}
		else
		{
			if ((_fmi_uFMIReferenceClock % 25000) == 0)
				rate = rate - 1;
		}
		outpw(REG_CLKDIV1, (inpw(REG_CLKDIV1) & 0x00ff) | (rate << 8));		// bit8 ~ 15

		/* init SD interface */
		fmiGet_SD_info(pSD1, &SD_DiskInfo);

		if (fmiSelectCard(pSD1))
			return FMI_SD_SELECT_ERROR;
	}

	/* do erase all */
//	printf("%d\n", SdChipErase(pSD0, &SD_DiskInfo));

	/* 
	 * Create physical disk descriptor 
	 */
	pDisk = malloc(sizeof(PDISK_T));
	memset((char *)pDisk, 0, sizeof(PDISK_T));

	/* read Disk information */
	pDisk->szManufacture[0] = '\0';
	strcpy(pDisk->szProduct, (char *)SD_DiskInfo.product);
	strcpy(pDisk->szSerialNo, (char *)SD_DiskInfo.serial);

	pDisk->nDiskType = DISK_TYPE_SD_MMC;
	pDisk->nPartitionN = 0;
	pDisk->ptPartList = NULL;
	
	pDisk->nSectorSize = 512;
	pDisk->uTotalSectorN = SD_DiskInfo.totalSectorN;
	pDisk->uDiskSize = SD_DiskInfo.diskSize;

	/* create relationship between UMAS device and file system hard disk device */
	
	pDisk->ptDriver = &_SDDiskDriver;

#ifdef DEBUG
	printf("SD disk found: size=%d MB\n", (int)pDisk->uDiskSize / 1024);
#endif

	if (uChipSel == 0)
		pDisk_SD0 = pDisk;
	else
		pDisk_SD1 = pDisk;

	fsPhysicalDiskConnected(pDisk);

	return pDisk->uTotalSectorN;

} 	/* end InitIDEDevice */

INT  fmiSD_Read(UINT32 uChipSel, UINT32 uSector, UINT32 uBufcnt, UINT32 uDAddr)
{
	int status=0;

	// enable SD
	outpw(REG_FMICSR, 0x02);

	if (uChipSel == 0)
	{
		outpw(REG_SDCSR, inpw(REG_SDCSR) & 0x9fffffff);
		status = fmiSD_Read_in(pSD0, uSector, uBufcnt, uDAddr);
	}
	else if (uChipSel == 1)
	{
		outpw(REG_SDCSR, (inpw(REG_SDCSR) & 0x9fffffff) | 0x40000000);
		status = fmiSD_Read_in(pSD1, uSector, uBufcnt, uDAddr);
	}
	return status;
}

INT  fmiSD_Write(UINT32 uChipSel, UINT32 uSector, UINT32 uBufcnt, UINT32 uSAddr)
{
	int status=0;

	// enable SD
	outpw(REG_FMICSR, 0x02);

	if (uChipSel == 0)
	{
		outpw(REG_SDCSR, inpw(REG_SDCSR) & 0x9fffffff);
		status = fmiSD_Write_in(pSD0, uSector, uBufcnt, uSAddr);
	}
	else if (uChipSel == 1)
	{
		outpw(REG_SDCSR, (inpw(REG_SDCSR) & 0x9fffffff) | 0x40000000);
		status = fmiSD_Write_in(pSD1, uSector, uBufcnt, uSAddr);
	}
	return status;
}




